if ( 'FBDEditor' in window ) ; else FBDEditor = {};
/*
    FBEditor for FBDEditor 1.0
    Copyright (C) 2008-2009 Yuriy Butenko y.v.butenko@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
FBDEditor.FBEditor = function ( prnt, winView, cntrl ) {
	var controller = new (function () {
		var bd = {};
		var selItem = null;
		var selCell = null, selCellTp = "i";
		var saved = true;
		var redrawInput = function () {
			saved = false;
			view.editCell( selCellTp == "o", selCell, selItem[selCellTp][selCell] || "" );
		};
		var saveValue = function () {
			if ( saved || !selItem ) return;
			saved = !saved;
			var v = view.getValue();
			if ( v ) selItem[selCellTp][selCell] = v;
			else delete selItem[selCellTp].splice(selCell,1);
		};
		var setCoord = function ( i, j ) {
			if ( !selItem ) return;
			if ( j < 0 ) return;
			var v = view.getValue();
			var l = selItem[i].length - ( i == selCellTp && !v) + ( i == selCellTp && v && !selItem[selCellTp][selCell]);
			if ( j > l ) return;
			selItem[selCellTp][selCell] = v;
			if ( !v ) selItem[selCellTp].splice(selCell,1);
			selCellTp = i;
			selCell = j;
			if ( !( selCell in selItem[selCellTp] ) ) selItem[selCellTp][selCell] = "";
			view.drawFB( selItem.i, selItem.o );
			redrawInput();
		};
		this.send = function ( event ) {
			switch ( event ) {
				case "fbclick":
					var h = arguments[1];
					view.clearCanvas();
					saveValue();
					if ( view.isPressed( h ) ) {
						view.unselect( h );
						selItem = null;
					} else {
						selItem && view.unselect( selItem.v );
						selItem = view.getAttr( view.select( h ) );
						selCellTp = "i";
						selCell=0;
						view.drawFB( selItem.i, selItem.o );
						redrawInput();
					};
					break;
				case "delselected":
					if ( selItem ) this.del( selItem.id );
					break;
				case "newblock":
					saveValue();
					var id = arguments[1];
					if ( id in bd ) {
						view.confirmAddBlock( id );
						break;
					};
				case "forcenewblock":
					this.send( "fbclick", this.add( arguments[1], [], [] ).v );
					break;
				case "cellClick":
					setCoord(arguments[1]?"o":"i",arguments[2]);
					break;
				case "editKeyExit":
					redrawInput();
					break;
				case "editKeyUp":
					setCoord(selCellTp,selCell-1);
					break;
				case "editKeyNext":
					setCoord(selCellTp == "i" ? "o" : "i",selCell);
					break;
				case "editKeyDown":
					setCoord(selCellTp,selCell+1);
					break;
				case "delCell":
					if ( selCell == selItem[selCellTp].length - 1 ) selItem[selCellTp][selCell] = "";
					else selItem[selCellTp].splice(selCell,1);
					view.drawFB( selItem.i, selItem.o );
					redrawInput();
					break;
				case "insCellUp":
					if ( !view.getValue() ) break;
					selItem[selCellTp].splice(selCell,0,"");
					selCell++;
					setCoord(selCellTp,selCell-1);
					break;
				case "insCellDown":
					if ( !view.getValue() ) break;
					selItem[selCellTp].splice(selCell+1,0,"");
					setCoord(selCellTp,selCell+1);
					break;
				case "accept":
					setCoord(selCellTp,selCell);
					cntrl.send("acceptFBEdit");
					break;
				case "ok":
					setCoord(selCellTp,selCell);
					cntrl.send("okFBEdit");
					break;
				case "cancel":
					setCoord(selCellTp,selCell);
					cntrl.send("cancelFBEdit");
					break;
			};
		};
		this.add = function ( id, inp, outp ) {
			id in bd && this.del( id );
			bd[id] = { 'id':id, 'i':inp, 'o':outp, 'v':view.add( id ) };
			view.setAttr( bd[id].v, bd[id] );
			return bd[id];
		};
		this.del = function ( id ) {
			if ( selItem && id == selItem.id ) {
				view.clearCanvas();
				selItem = null;
				view.unselect( bd[id].v );
			};
			view.del( bd[id].v );
			delete bd[id];
		};
		this.getDef = function () {
			var res = {};
			for ( var i in bd ) res[i] = {'inputs':bd[i].i.slice(0),'outputs':bd[i].o.slice(0)};
			return res;
		};
		this.setDef = function ( blks ) {
			for ( var i in bd ) this.del( i );	// delete all
			for ( var i in blks ) this.add( i, blks[i].inputs, blks[i].outputs );
			return blks;
		};
	})();
	var view = new (function () {
		var w = new winView.win();
		w.setCaption('FB definitions');
		w.setDim(500,320);
		w.setPos( w.centerX(), w.centerY() );
		w.resize = true;
		w.setResize();
		w.content.innerHTML = '<div class="FBDEditor_FBEditor_list"></div>'
			+'<div class="FBDEditor_FBEditor_canvas">'
				+'<div class="FBDEditor_FBEditor_canvasFB">'
					+'<div class="FBDEditor_FBEditor_canvasFBHeader"></div>'
					+'<div class="FBDEditor_FBEditor_canvasFBFooter"></div>'
					+'<div class="FBDEditor_FBEditor_canvasInputs"></div>'
					+'<div class="FBDEditor_FBEditor_canvasOutputs"></div>'
					+'<input id="FBDEditor_fbeditInput" class="FBDEditor_FBEditor_canvasCursor"/>'
					+'<div class="FBDEditor_FBEditor_canvasInputsEdit">'
						+'<a class="FBDEditor_FBEditor_insCellUp" href=""></a>'
						+'<a class="FBDEditor_FBEditor_delCell" href=""></a>'
						+'<a class="FBDEditor_FBEditor_insCellDown" href=""></a>'
					+'</div>'
				+'</div>'
			+'</div>'
			+'<div class="FBDEditor_FBEditor_buttons"><div></div><div></div><div></div><hr><div></div><div></div></div>';
		w.content.className += " FBDEditor_FBEditor_content";
		var fbdiv = w.content.childNodes[1].firstChild;	// childNodes for IE :)
		var fbinput = fbdiv.childNodes[4];	// childNodes for IE :)
		var fbInputEdit = fbdiv.childNodes[5];	// childNodes for IE :)
		function crtB ( n, txt ) {
			var b = new winView.button();
			b.content.innerHTML = txt;
			w.content.childNodes[2].childNodes[n].appendChild(b.e);	// childNodes for IE :)
			w.content.childNodes[2].childNodes[n].className = "yWin_ButtonContainer";
			return b;
		};
		var bAccept = crtB( 0, '&nbsp;&nbsp;Accept&nbsp;&nbsp;' );
		var bOk = crtB( 1, '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' );
		var bCancel = crtB( 2, '&nbsp;&nbsp;Cancel&nbsp;&nbsp;' );
		var bNew = crtB( 4, '&nbsp;&nbsp;&nbsp;New&nbsp;&nbsp;&nbsp;' );
		var bDel = crtB( 5, '&nbsp;&nbsp;Delete&nbsp;&nbsp;' );
		bAccept.onclick = function () {
			controller.send("accept");
			bAccept.setOff();
		};
		bOk.onclick = function () {
			controller.send("ok");
			bOk.setOff();
		};
		w.close = bCancel.onclick = function () {
			controller.send("cancel");
			bCancel.setOff();
		};
		var fbCounter = 1;
		bNew.onclick = function () {
			winView.prompt('New function block','<p style="font-size:14px;">Enter name of function block</p>', 'FB'+(fbCounter++), null, putNewBlock, 300);
		};
		var putNewBlock = function ( x ) {
			if ( x ) controller.send("newblock",x);
			bNew.setOff();
		};
		bDel.onclick = function () {
			controller.send("delselected");
			bDel.setOff();
		};
		var onclick = function () {
			controller.send('fbclick',this);
			return false;
		};
		this.add = function ( id ) {
			var h = document.createElement( "a" );
			w.content.childNodes[0].appendChild( h );
			h.appendChild( document.createTextNode( id ) );
			h.setAttribute("href","");
			h.onclick = onclick;
			return h;
		};
		this.del = function ( h ) {
			h.parentNode.removeChild( h );
		};
		this.select = function ( h ) {
			h.className = "Pressed";
			return h;
		};
		this.unselect = function ( h ) {
			h.className = "";
			return h;
		};
		this.isPressed = function ( h ) {
			return !!h.className;
		};
		var lockHandle = null;
		this.on = function () {
			lockHandle = winView.lock( w, w.e );
			w.on();
		};
		this.off = function () {
			w.off();
			winView.unlock(lockHandle);
		};
		this.confirmAddBlock = function ( id ) {
			winView.confirm(
				'duplicate name',
				'<p style="font-size:14px;">Duplicate block name "' + id + '". Redefine?</p>',
				null,
				function ( res ) {
					if ( res ) controller.send("forcenewblock",id);
				},
				300);
		};
		this.setAttr = function ( h, d ) {
			h.FBDEdit = d;
			return h;
		};
		this.getAttr = function ( h ) {
			return h.FBDEdit;
		};
		fbInputEdit.firstChild.onclick = fbInputEdit.lastChild.onclick = function () {
			controller.send( this.className == "FBDEditor_FBEditor_insCellUp" ? "insCellUp" : "insCellDown" );
			return false;
		};
		fbInputEdit.firstChild.nextSibling.onclick = deleteCell = function () {
			controller.send( "delCell" );
			return false;
		};
		fbInputEdit.firstChild.title = fbInputEdit.lastChild.title = "insert cell";
		fbInputEdit.firstChild.nextSibling.title = "delete cell";
		var fbClick = function () {
			var i = 0, x = this;
			while ( x = x.previousSibling ) i++;
			controller.send("cellClick",this.parentNode == fbdiv.childNodes[3],i);
		};
		var parser = /\s*(\S+)\s*/;
		this.getValue = function () {
			var r = parser.exec(fbinput.value);
			return r && r[1] && r[1] != "-" ? r[1] : "";
		};
		this.editCell = function ( isOut, n, v ) {
			fbinput.style.left = isOut ? "132px" : "4px";
			fbInputEdit.style.left = isOut ? "228px" : "100px";
			fbinput.style.top = fbInputEdit.style.top = n*32 + "px";
			fbinput.blur();
			fbinput.value = v;
			fbinput.select();
			fbinput.focus();
		};
		fbinput.onkeydown = function ( e ) {
			var v = fbinput.value;
			switch ( e ? e.keyCode : window.event.keyCode ) {
				case 38:
					controller.send("editKeyUp");
					return false;
				case 40:
				case 13:
					controller.send("editKeyDown");
					return false;
				case 27:
					controller.send("editKeyExit");
					return false;
				case 9:
					controller.send("editKeyNext");
					return false;
			};
		};
		this.drawFB = function ( inp, outp ) {
			editCell = null;
			var si = "", so = "", len = Math.max(inp.length,outp.length,1);
			for ( var i = 0; i < len; i++ ) si += '<div>' + ( inp[i] || "-" ) + '</div>';
			fbdiv.childNodes[2].innerHTML = si;
			for ( var i = fbdiv.childNodes[2].firstChild; i; i = i.nextSibling ) i.onclick = fbClick;
			for ( var i = 0; i < len; i++ ) so += '<div>' + ( outp[i] || "-" ) + '</div>';
			fbdiv.childNodes[3].innerHTML = so;
			for ( var i = fbdiv.childNodes[3].firstChild; i; i = i.nextSibling ) i.onclick = fbClick;
			fbdiv.style.display = "";
			fbdiv.childNodes[1].style.top = fbdiv.style.height = len * 32 + "px";
			fbdiv.childNodes[1].className = fbdiv.childNodes[1].className.replace( "Odd", "" );
			if ( len % 2 ) fbdiv.childNodes[1].className += "Odd";
			fbdiv.style.top = Math.max( fbdiv.parentNode.offsetHeight - fbdiv.offsetHeight >> 1, 16 ) + "px";
		};
		this.clearCanvas = function () {
			fbdiv.style.display = "none";
		};
		this.on();
		this.clearCanvas();
	})();
	this.add = function ( id, inputs, outputs ) {
		var z = controller.add( id, inputs, outputs );
		return z;
	};
	this.del = function ( id ) {
		return controller.del( id );
	};
	this.on = function () {
		view.on();
	};
	this.off = function () {
		view.off();
	};
	this.getDef = function () {
		return controller.getDef();
	};
	this.setDef = function ( blks ) {
		return controller.setDef( blks );
	};
}
